#pragma once
#include <iostream>
#include <pthread.h>
#include <unistd.h>
#include <functional>
#include <string>

namespace ThreadModule
{
    template<typename Func, typename ...Args>
    class Thread
    {
        using RetType = std::invoke_result_t<Func, Args...>;
        using FunctionType = std::function<RetType(Args...)>;
    public:
        Thread(Func&& func, Args&&... args)
            : _func(new FunctionType(std::bind(std::forward<Func>(func), std::forward<Args>(std::ref(args))...)))
        {
            _Start();
        }
        Thread(const FunctionType* function): _func(function)
        {
            _Start();
        }

        ~Thread()
        {
            if(_is_start && !_is_join)
            {
                pthread_detach(_tid);
            }
            delete _func;
        }
    public:
        void* Join()
        {
            if(!_is_start || _is_join) { return nullptr; }

            void* ret = nullptr;
            int n = pthread_join(_tid, &ret);
            if(n != 0) { perror("error: Join()::pthread_join()"); exit(1);}

            _is_join = true;
            return ret;
        }

    private:
        // The purpose of declaring static is to prevent function arguments from passing this pointer by default, 
            // which does not meet the pthread_create call condition
        static void* handler(void* f)
        {
            auto p = static_cast<std::function<RetType()>*>(f);
            auto ret = (*p)();   // call _func function
            return new RetType(ret);
        }
        bool _Start()
        {
            int n = pthread_create(&_tid, nullptr, handler, _func);
            _is_start = (n == 0)? true: false;
            return _is_start;
        }

    private:
        pthread_t _tid;
        std::function<RetType(Args...)>* _func;
        bool _is_start = false;
        bool _is_join = false;
    };

    // template<typename Func, typename ...Args>
    // class ThreadData
    // {
    // public:
    //     ThreadData(std::string name, Func&& func, Args&&... args): 
    //         _thread(std::bind(std::forward<Func>(func), std::forward<Args>(args)...)), _name(name) {}

    //     ~ThreadData() = default;
    // public:
    //     Thread<Func, Args...>* getThreadPtr()
    //     {
    //         return &_thread;
    //     }
    //     std::string getName()
    //     {
    //         return _name;
    //     }

    // private:
    //     std::string _name;
    //     Thread<Func, Args...> _thread;
    // };
}


